package top.jfunc.common.db.data;

import java.util.LinkedHashMap;
import java.util.Map;

import top.jfunc.common.utils.StrUtil;

/**
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
 * @since 1.0 2022/11/24 19:17
 */
public abstract class PagingQueryProvider {

  private String selectClause;

  private String fromClause;

  private String whereClause;

  private Map<String, Order> sortKeys = new LinkedHashMap<>();

  private String groupClause;

  /**
   * The setter for the group by clause
   *
   * @param groupClause SQL GROUP BY clause part of the SQL query string
   */
  public void setGroupClause(String groupClause) {
    if (StrUtil.isNotBlank(groupClause)) {
      this.groupClause = removeKeyWord("group by", groupClause);
    }
    else {
      this.groupClause = null;
    }
  }

  /**
   * The getter for the group by clause
   *
   * @return SQL GROUP BY clause part of the SQL query string
   */
  public String getGroupClause() {
    return this.groupClause;
  }

  /**
   * @param selectClause SELECT clause part of SQL query string
   */
  public void setSelectClause(String selectClause) {
    this.selectClause = removeKeyWord("select", selectClause);
  }

  /**
   * @return SQL SELECT clause part of SQL query string
   */
  protected String getSelectClause() {
    return selectClause;
  }

  /**
   * @param fromClause FROM clause part of SQL query string
   */
  public void setFromClause(String fromClause) {
    this.fromClause = removeKeyWord("from", fromClause);
  }

  /**
   * @return SQL FROM clause part of SQL query string
   */
  protected String getFromClause() {
    return fromClause;
  }

  /**
   * @param whereClause WHERE clause part of SQL query string
   */
  public void setWhereClause(String whereClause) {
    if (StrUtil.isNotBlank(whereClause)) {
      this.whereClause = removeKeyWord("where", whereClause);
    }
    else {
      this.whereClause = null;
    }
  }

  /**
   * @return SQL WHERE clause part of SQL query string
   */
  protected String getWhereClause() {
    return whereClause;
  }

  /**
   * @param sortKeys key to use to sort and limit page content
   */
  public void setSortKeys(Map<String, Order> sortKeys) {
    this.sortKeys = sortKeys;
  }

  /**
   * A Map&lt;String, Boolean&gt; of sort columns as the key and boolean for ascending/descending (ascending = true).
   *
   * @return sortKey key to use to sort and limit page content
   */
  public Map<String, Order> getSortKeys() {
    return sortKeys;
  }

  /**
   * The sort key placeholder will vary depending on whether named parameters
   * or traditional placeholders are used in query strings.
   *
   * @return place holder for sortKey.
   */
  public String getSortKeyPlaceHolder(String keyName) {
    return   "?";
  }

  /**
   * Method generating the query string to be used for retrieving the first
   * page. This method must be implemented in sub classes.
   *
   * @param pageSize number of rows to read per page
   * @return query string
   */
  public abstract String generateFirstPageQuery(int pageSize);

  /**
   * Method generating the query string to be used for retrieving the pages
   * following the first page. This method must be implemented in sub classes.
   *
   * @param pageSize number of rows to read per page
   * @return query string
   */
  public abstract String generateRemainingPagesQuery(int pageSize);

  /**
   * Method generating the query string to be used for jumping to a specific
   * item position. This method must be implemented in sub classes.
   *
   * @param itemIndex the index of the item to jump to
   * @param pageSize number of rows to read per page
   * @return query string
   */
  public abstract String generateJumpToItemQuery(int itemIndex, int pageSize);

  private String removeKeyWord(String keyWord, String clause) {
    String temp = clause.trim();
    int length = keyWord.length();
    if (temp.toLowerCase().startsWith(keyWord) && Character.isWhitespace(temp.charAt(length)) && temp.length() > length + 1) {
      return temp.substring(length + 1);
    }
    else {
      return temp;
    }
  }

  /**
   * @return sortKey key to use to sort and limit page content (without alias)
   */
  public Map<String, Order> getSortKeysWithoutAliases() {
    Map<String, Order> sortKeysWithoutAliases = new LinkedHashMap<>();

    for (Map.Entry<String, Order> sortKeyEntry : sortKeys.entrySet()) {
      String key = sortKeyEntry.getKey();
      int separator = key.indexOf('.');
      if (separator > 0) {
        int columnIndex = separator + 1;
        if (columnIndex < key.length()) {
          sortKeysWithoutAliases.put(key.substring(columnIndex), sortKeyEntry.getValue());
        }
      }
      else {
        sortKeysWithoutAliases.put(sortKeyEntry.getKey(), sortKeyEntry.getValue());
      }
    }

    return sortKeysWithoutAliases;
  }
}
